// SPDX-FileCopyrightText: Copyright 2025-2025 深圳市同心圆网络有限公司
// SPDX-License-Identifier: Apache-2.0

package user_api_serv

import (
	"context"
	"time"

	"gitcode.com/lvupclub/lvup-api/config"
	"gitcode.com/lvupclub/lvup-api/dao"
	"gitcode.com/lvupclub/lvup-api/dao/user_dao"
	"gitcode.com/lvupclub/lvup-api/notice_serv"
	"gitcode.com/lvupclub/proto-gen-go.git/src/user_api"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/known/anypb"
)

type UserApiImpl struct {
	user_api.UnimplementedUserApiServer
	loginCfg *config.LoginCfg
}

func NewUserApiImpl(loginCfg *config.LoginCfg) *UserApiImpl {
	return &UserApiImpl{loginCfg: loginCfg}
}

func (apiImpl *UserApiImpl) Login(ctx context.Context, req *user_api.LoginRequest) (*user_api.LoginResponse, error) {
	switch req.UserType {
	case user_api.USER_TYPE_USER_TYPE_LOCAL:
		return apiImpl.loginByLocal(ctx, req.UserName, req.PasswdOrCode)
	case user_api.USER_TYPE_USER_TYPE_WECHAT:
		return apiImpl.loginByWechat(ctx, req.PasswdOrCode)
	default:
		return &user_api.LoginResponse{
			Code:   user_api.LoginResponse_CODE_WRONG_USERNAME,
			ErrMsg: "用户不存在",
			UserInfo: &user_api.UserInfo{
				BasicInfo: &user_api.BasicUserInfo{},
			},
		}, nil
	}
}

func (apiImpl *UserApiImpl) Logout(ctx context.Context, req *user_api.LogoutRequest) (*user_api.LogoutResponse, error) {
	dao.CacheDao.RemoveSession(ctx, req.SessionId)

	return &user_api.LogoutResponse{
		Code: user_api.LogoutResponse_CODE_OK,
	}, nil
}

func (apiImpl *UserApiImpl) UpdateBasic(ctx context.Context, req *user_api.UpdateBasicRequest) (*user_api.UpdateBasicResponse, error) {
	sessItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &user_api.UpdateBasicResponse{
			Code:   user_api.UpdateBasicResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}

	userItem, err := user_dao.UserInfoDao.GetById(ctx, sessItem.UserId)
	if err != nil {
		return nil, err
	}

	nowTime := time.Now().UnixMilli()

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx context.Context) (interface{}, error) {
		var newBasicInfo *user_dao.BasicUserInfoDbItem
		if userItem.UserType == user_api.USER_TYPE_USER_TYPE_LOCAL {
			newBasicInfo = &user_dao.BasicUserInfoDbItem{
				DisplayName:  req.BasicInfo.DisplayName,
				LogoUri:      req.BasicInfo.LogoUri,
				UserRole:     req.BasicInfo.UserRole,
				PublicAccess: req.BasicInfo.PublicAccess,
				UserIntro:    req.BasicInfo.UserIntro,
			}
		} else { //微信账号不能修改昵称
			newBasicInfo = &user_dao.BasicUserInfoDbItem{
				DisplayName:  userItem.BasicInfo.DisplayName,
				LogoUri:      userItem.BasicInfo.LogoUri,
				UserRole:     req.BasicInfo.UserRole,
				PublicAccess: req.BasicInfo.PublicAccess,
				UserIntro:    req.BasicInfo.UserIntro,
			}
		}
		err = user_dao.UserInfoDao.UpdateBasic(sessCtx, sessItem.UserId, newBasicInfo, nowTime)
		if err != nil {
			return nil, err
		}
		return &user_api.UpdateBasicResponse{
			Code: user_api.UpdateBasicResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	return ret.(*user_api.UpdateBasicResponse), nil
}

func (apiImpl *UserApiImpl) ChangePasswd(ctx context.Context, req *user_api.ChangePasswdRequest) (*user_api.ChangePasswdResponse, error) {
	sessItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &user_api.ChangePasswdResponse{
			Code:   user_api.ChangePasswdResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	if sessItem.UserType != user_api.USER_TYPE_USER_TYPE_LOCAL {
		return &user_api.ChangePasswdResponse{
			Code:   user_api.ChangePasswdResponse_CODE_NOT_LOCAL_USER,
			ErrMsg: "会话不存在",
		}, nil
	}

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx context.Context) (interface{}, error) {
		err = user_dao.UserPasswdDao.ChangePasswd(sessCtx, sessItem.UserId, req.OldPasswd, req.NewPasswd)
		if err != nil {
			return nil, err
		}
		return &user_api.ChangePasswdResponse{
			Code: user_api.ChangePasswdResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	return ret.(*user_api.ChangePasswdResponse), nil
}

func (apiImpl *UserApiImpl) KeepAlive(ctx context.Context, req *user_api.KeepAliveRequest) (*user_api.KeepAliveResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &user_api.KeepAliveResponse{
			Code:   user_api.KeepAliveResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}

	dao.CacheDao.KeepSessionAlive(ctx, req.SessionId, sessionItem.UserId)
	return &user_api.KeepAliveResponse{
		Code: user_api.KeepAliveResponse_CODE_OK,
	}, nil
}

func (apiImpl *UserApiImpl) PullNotice(req *user_api.PullNoticeRequest, s user_api.UserApi_PullNoticeServer) error {
	ctx := s.Context()
	sessItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return s.Send(&user_api.PullNoticeResponse{
			Code:       user_api.PullNoticeResponse_CODE_WRONG_SESSION,
			ErrMsg:     "会话不存在",
			NoticeData: &anypb.Any{},
		})
	}
	subCh, err := notice_serv.SubscribeToUser(ctx, sessItem.UserId)
	if err != nil {
		return err
	}
	defer subCh.Close()

	msgCh := subCh.Channel()
	for {
		select {
		case msg := <-msgCh:
			notice := anypb.Any{}
			err = proto.Unmarshal([]byte(msg.Payload), &notice)
			if err != nil {
				continue
			}
			err = s.Send(&user_api.PullNoticeResponse{
				Code:       user_api.PullNoticeResponse_CODE_OK,
				NoticeData: &notice,
			})
			if err != nil {
				return err
			}
		case <-time.After(5 * time.Minute):
			_, ok = dao.CacheDao.GetSession(ctx, req.SessionId)
			if !ok {
				return s.Send(&user_api.PullNoticeResponse{
					Code:       user_api.PullNoticeResponse_CODE_WRONG_SESSION,
					ErrMsg:     "会话不存在",
					NoticeData: &anypb.Any{},
				})
			}
		}

	}
}

func (apiImpl *UserApiImpl) GetPubInfo(ctx context.Context, req *user_api.GetPubInfoRequest) (*user_api.GetPubInfoResponse, error) {
	emptyInfo := &user_api.UserInfo{
		BasicInfo: &user_api.BasicUserInfo{},
	}

	userItem, err := user_dao.UserInfoDao.GetById(ctx, req.UserId)
	if err != nil {
		return &user_api.GetPubInfoResponse{
			Code:     user_api.GetPubInfoResponse_CODE_NOT_ALLOW,
			ErrMsg:   "不允许访问用户信息",
			UserInfo: emptyInfo,
		}, nil
	}
	if !userItem.BasicInfo.PublicAccess {
		return &user_api.GetPubInfoResponse{
			Code:     user_api.GetPubInfoResponse_CODE_NOT_ALLOW,
			ErrMsg:   "不允许访问用户信息",
			UserInfo: emptyInfo,
		}, nil
	}
	return &user_api.GetPubInfoResponse{
		Code:     user_api.GetPubInfoResponse_CODE_OK,
		UserInfo: userItem.ToUserInfo(),
	}, nil
}
